﻿using System;
using System.Threading;

namespace NMetronome
{
	public class MetronomeTicker : IDisposable
	{
		public MetronomeTicker(IBeep beepService)
		{
			this.beepService = beepService;
			this.tickThread = new Thread(this.Runner);
		}

		public int BPM
		{
			set
			{
				sleepTime = (int)60000 / value;
				if (isRunning)
				{
					// just to refresh, if BPM was very low
					Stop();
					Start();
				}
			}
		}

		/// <summary>
		/// Gets or sets the beep pattern.
		/// for now, -1 is always high
		/// 1 is always low
		/// 2 - x: 1 high, rest low
		/// </summary>
		/// <value>The beep pattern.</value>
		public int BeepPattern
		{
			get;
			set;
		}

		private bool isRunning = false;
		private int sleepTime = 1000;

		private IBeep beepService;

		private Thread tickThread;

		public void Start()
		{
			if (isRunning) // already running
				return;
			isRunning = true;

			if (tickThread.ThreadState == ThreadState.Unstarted)
				tickThread.Start();
		}

		public void Stop()
		{
			isRunning = false;
		}

		private int beepCount = 0;
		private void Runner()
		{
			while (true)
			{
				if (isRunning)
				{
					// TODO tick could take very long (wav-file) so use an own thread??

					if (BeepPattern == -1)
						beepService.BeepHigh();
					else if (BeepPattern == 0)
						beepService.BeepLow();
					else
					{
						if (beepCount % BeepPattern == 0)
							beepService.BeepHigh();
						else
							beepService.BeepLow();

						beepCount++;
						if (beepCount >= BeepPattern)
							beepCount = 0;
					}

					System.Threading.Thread.Sleep(sleepTime);
				}
				else
				{
					// just a nonblocking wait
					System.Threading.Thread.Sleep(500);
				}
			}
		}

		#region IDisposable Pattern

		/// <summary>
		/// Release all resources held by this object.
		/// </summary>
		/// <remarks>
		/// Dispose can safely be called multiple times, but the object cannot be used for anything else.
		/// </remarks>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Finalize the object.
		/// </summary>
		~MetronomeTicker()
		{
			Dispose(false);
		}

		/// <summary>
		/// Protected overload of Dispose that is standard in IDisposable patterns.
		/// </summary>
		/// <param name="disposing">Whether or not this is being called by Dispose, rather than by the finalizer.</param>
		/// <remarks>
		/// Overrides of this method should always call base.Dispose.
		/// </remarks>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				tickThread.Abort();
			}
		}

		#endregion
	}
}
